local FightExcuter = class("FightExcuter") -- 战斗执行器
local BattleReport = require("gameLogic.fightEntity.BattleReport")
local AttributeConst = require("const.AttributeConst")
local FightConst = require("const.FightConst")

function FightExcuter:ctor()
    self._fightEntity = {}
    self._callEntity = {}
    self._roundCount = 0
    self._round = {}
    self._fightEntityId = 0
    self._buffManager = require("gameLogic.fight.fightBuff.BuffManager").new(self)
    self._fightEffectManager = require("gameLogic.fight.fightEffect.FightEffectManager").new(self)
    self:_initData()
end

function FightExcuter:initFightEffect(effects, entity)
    for i = 1, #effects do
        self._fightEffectManager:addFightEffect(effects[i], entity)
    end
end

function FightExcuter:_initData()
    self._callAppPos = {}
    self._callAppPos[FightConst.FRIEND_STANDING] = {
        {row = 3, col = 1, use = false},
        {row = 3, col = 2, use = false},
        {row = 3, col = 3, use = false}
    }
    self._callAppPos[FightConst.ENEMY_STANDING] = {
        {row = 3, col = 1, use = false},
        {row = 3, col = 2, use = false},
        {row = 3, col = 3, use = false}
    }
    self._callSidPos = {}
    self._callSidPos[FightConst.FRIEND_STANDING] = {
        {row = 4, col = -1, use = false},
        {row = 4, col = 4, use = false}
    }
    self._callSidPos[FightConst.ENEMY_STANDING] = {
        {row = 4, col = -1, use = false},
        {row = 4, col = 4, use = false}
    }
    self._forwardCenter = {}
    self._forwardCenter[FightConst.FRIEND_STANDING] = {
        {row = 5, col = 2, use = false}
    }
    self._forwardCenter[FightConst.ENEMY_STANDING] = {
        {row = 5, col = 2, use = false}
    }
    self._posData = {}
    self._posData[FightConst.CALL_OBJ_POS_CEN] = self._forwardCenter
    self._posData[FightConst.CALL_OBJ_POS_SID] = self._callSidPos
    self._posData[FightConst.CALL_OBJ_POS_APP] = self._callAppPos
end

function FightExcuter:getUnusedStand(posType, camp)
    local list = self._posData[posType][camp]
    for i = 1, #list do
        if list[i].use == false then
            return list[i]
        end
    end
    return list[1]
end

function FightExcuter:getFightEntityId()
    self._fightEntityId = self._fightEntityId + 1
    return self._fightEntityId
end

function FightExcuter:_init()
end

function FightExcuter:addEntity(entity)
    table.insert(self._fightEntity, entity)
end

function FightExcuter:addCallEntity(entity)
    printDebug("新增召唤物 " .. entity:getId())
    table.insert(self._callEntity, entity)
    if entity:isFight() then
        table.insert(self._round, entity)
    end
end

function FightExcuter:getCallEntityByStandCamp(stand, camp)
    for i = 1, #self._callEntity do
        local entity = self._callEntity[i]
        if entity:getStandCol() == stand.col and entity:getStandRow() == stand.row and entity:getCamp() == camp then
            return entity
        end
    end
end

function FightExcuter:removeCallEntity(entityId)
    for i = 1, #self._callEntity do
        if self._callEntity[i]:getId() == entityId then
            table.remove(self._callEntity, i)
            return
        end
    end
end

function FightExcuter:getEntityById(entityId)
    for i = 1, #self._fightEntity do
        if self._fightEntity[i]:getId() == entityId then
            return self._fightEntity[i]
        end
    end
    for i = 1, #self._callEntity do
        if self._callEntity[i]:getId() == entityId then
            return self._callEntity[i]
        end
    end
end

function FightExcuter:getBuffManager()
    return self._buffManager
end

function FightExcuter:doNextEntity(report)
    local entity = self._round[1]
    self:_entityAction(entity, report)
    self._lastEntity = entity -- 上一次行动的单位
end

function FightExcuter:_entityAction(entity, report)
    self:entityActionBefore(entity, report)
    entity:registerEntityActionEvent(
        function(event)
            if event ~= "actionEnd" then
                return
            end
            self:entityActionAfter(entity, report)
            table.remove(self._round, 1)
            local endFlag = self:checkFightEnd()
            report:setEndFlag(endFlag)
            report:setRoundCount(self._roundCount)
            G_SignalManager:dispatch(SignalConst.EVENT_FIGHT_REPORT_DISPATCH, report)
        end,
        true
    )
    entity:action(report)
end

function FightExcuter:checkFightEnd()
    local result = {
        [FightConst.FRIEND_STANDING] = 0,
        [FightConst.ENEMY_STANDING] = 0
    }
    for i = 1, #self._fightEntity do
        local entity = self._fightEntity[i]
        if not entity:isDie() then
            result[entity:getCamp()] = result[entity:getCamp()] + 1
        end
    end
    if result[FightConst.FRIEND_STANDING] == 0 then
        return 1
    end
    if result[FightConst.ENEMY_STANDING] == 0 then
        return 2
    end
    return 0
end

function FightExcuter:collectEntityBlood()
    local bloods = {}
    for i = 1, #self._fightEntity do
        local entity = self._fightEntity[i]
        local row = entity:getStandRow()
        local col = entity:getStandCol()
        local camp = entity:getCamp()
        local blood = entity:getAttrValue(AttributeConst.BLOOD)
        local maxBlood = entity:getAttrValue(AttributeConst.INIT_BLOOD)
        bloods[camp .. "_" .. row .. "_" .. col] = blood .. "/" .. maxBlood
    end
    return bloods
end

function FightExcuter:entityActionBefore(entity, report)
    local battle = report:newActionBefore()
    self._buffManager:actionBeforeBuff(battle, entity)
end

function FightExcuter:entityActionAfter(entity, report)
    local battle = report:newActionAfter()
    self._buffManager:actionAfterBuff(battle, entity)
end

function FightExcuter:roundBefore(report)
    local reportRound = report:newRoundBefore()
    self._buffManager:roundBeforeBuff(reportRound)
    self:checkCallEntity(reportRound)
    G_SignalManager:dispatch(SignalConst.EVENT_ROUND_BEGIN, self._roundCount, reportRound)
end

function FightExcuter:checkCallEntity(report)
    for i = #self._callEntity, 1, -1 do
        self._callEntity[i]:check(report)
    end
end

function FightExcuter:start()
    self._signalFightNext = G_SignalManager:add(SignalConst.EVENT_FIGHT_NEXT, handler(self, self._onEventFightNext))
end

function FightExcuter:nextRound()
    local report = BattleReport.new()
    self._round = {}
    self._roundCall = {}
    self._roundCount = self._roundCount + 1
    self:roundBefore(report)
    for i = 1, #self._fightEntity do
        if not self._fightEntity[i]:isDie() then
            table.insert(self._round, self._fightEntity[i])
        end
    end
    for i = 1, #self._callEntity do
        if not self._callEntity[i]:isDie() then
            if self._callEntity[i]:isFight() then
                table.insert(self._round, self._callEntity[i])
            end
        end
    end
    table.sort(
        self._round,
        function(a, b)
            local speeda = a:getAttrValue(AttributeConst.SPEED)
            local speedb = b:getAttrValue(AttributeConst.SPEED)
            return speeda > speedb
        end
    )
    report:setRoundCount(self._roundCount)
    G_SignalManager:dispatch(SignalConst.EVENT_FIGHT_REPORT_DISPATCH, report)
end

function FightExcuter:_onEventFightNext()
    if self:_checkPreNext() then
        return
    end
    while (self._round[1] and self._round[1]:isDie()) do
        table.remove(self._round, 1)
    end
    if #self._round == 0 then
        self:nextRound()
        return
    end
    self:_resetRoundCall()
    local report = BattleReport.new()
    self:doNextEntity(report)
end

function FightExcuter:_resetRoundCall()
    self._roundCall = {}
    for i = 1, #self._callEntity do
        if not self._callEntity[i]:isDie() and not self._callEntity[i]:isFight() then
            table.insert(self._roundCall, self._callEntity[i])
        end
    end
end

function FightExcuter:_checkPreNext()
    if not self._lastEntity or self._lastEntity:isDie() then
        return false
    end
    for i = 1, #self._roundCall do
        local entity = self._roundCall[i]
        if entity:getClassName() == FightConst.ENTITY_CLASS_COL and entity:checkCollaboration(self._lastEntity) then
            table.remove(self._roundCall, i)
            self:_doCollaborationEntity(entity)
            return true
        end
    end
    return false
end

function FightExcuter:recordLastBeAttack(entitys)
    self._lastBeAttackEntitys = entitys
end

function FightExcuter:getLastBeAttack()
    return self._lastBeAttackEntitys
end

function FightExcuter:_doCollaborationEntity(entity)
    local report = BattleReport.new()
    BattleReport:setRoundCount(self._roundCount)
    self:_entityAction(entity, report)
end

function FightExcuter:destroy()
    self._signalFightNext:remove()
    self._signalFightNext = nil
    self._buffManager:destroy()
    self._fightEffectManager:destroy()
end

function FightExcuter:getRoundCount()
    return self._roundCount
end

function FightExcuter:getHatredCalEntity(camp)
    local result = {}
    for i = 1, #self._callEntity do
        local entity = self._callEntity[i]
        if entity:getClassName() == FightConst.ENTITY_CLASS_STA and entity:checkHatred(camp) then
            table.insert(result, entity)
        end
    end
    return result
end

function FightExcuter:getEnemys(camp)
    local enemyCamp = camp
    if camp == FightConst.FRIEND_STANDING then
        enemyCamp = FightConst.ENEMY_STANDING
    else
        enemyCamp = FightConst.FRIEND_STANDING
    end
    return self:getFriends(enemyCamp)
end

function FightExcuter:getFriends(camp)
    local result = {}
    for i = 1, #self._fightEntity do
        local entity = self._fightEntity[i]
        if not entity:isDie() and entity:getCamp() == camp then
            table.insert(result, entity)
        end
    end
    for i = 1, #self._callEntity do
        local entity = self._callEntity[i]
        if not entity:isDie() and entity:getCamp() == camp and entity:isFight() then
            table.insert(result, entity)
        end
    end
    return result
end

return FightExcuter
